﻿//
// Copyright (c) 2024 BPer
//
#include <bperrtp/BPerRtcpNack.h>
#include <bperutil/sys/BPerLog.h>
#include <bperutil/sys/BPerMath.h>
#include <bperrtp/BPerRtpConstant.h>



void bPer_rtcpNack_clear(BPerRtcpNack *nack) {
	if(nack->vsize==0) return;
	bPer_memset(nack->nacks,0,nack->vsize * sizeof(uint16_t));
	nack->vsize=0;
}

void bPer_rtcpNack_addSn(BPerRtcpNack *nack, uint16_t sn) {
	if (nack == NULL)
		return;
	if (nack->vsize >= nack->capacity - 1) {
		uint16_t *tmp = (uint16_t*) bPer_calloc((nack->capacity + 50) * sizeof(uint16_t), 1);
		bPer_memcpy(tmp, nack->nacks, nack->vsize * sizeof(uint16_t));
		bPer_free(nack->nacks);
		nack->nacks = tmp;
		nack->capacity += 50;
	}
	bPer_insert_uint16_sort(nack->nacks, sn, &nack->vsize);

}

int32_t bPer_decode_rtcpNack(BPerRtcpCommon *comm, BPerBuffer *buffer) {
	if (comm == NULL)
		return 1;
	/*
	 @doc: https://tools.ietf.org/html/rfc4585#section-6.1
	 0                   1                   2                   3
	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |V=2|P|   FMT   |       PT      |          length               |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |                  SSRC of packet sender                        |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |                  SSRC of media source                         |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 :            Feedback Control Information (FCI)                 :
	 :                                                               :

	 Generic NACK
	 0                   1                   2                   3
	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |            PID                |             BLP               |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	int32_t err = BPer_Ok;
	comm->data = buffer->head;
	comm->nb_data = bPer_buffer_left(buffer);

	if (BPer_Ok != (err = bPer_decode_header_rtcpCommon(comm, buffer))) {
		return bPer_error_wrap(err, "decode header");
	}

    comm->nack->mediaSsrc = bPer_read_4bytes(buffer);
	char bitmask[20];
	int32_t i = 0;
	for (i = 0; i < (comm->header.length - 2); i++) {
		uint16_t pid = bPer_read_2bytes(buffer);
		uint16_t blp = bPer_read_2bytes(buffer);
		bPer_rtcpNack_addSn(comm->nack, pid);

		bPer_memset(bitmask, 0, 20);
		for (int32_t j = 0; j < 16; j++) {
			bitmask[j] = (blp & (1 << j)) >> j ? '1' : '0';

			if ((blp & (1 << j)) >> j)
				bPer_rtcpNack_addSn(comm->nack, pid + j + 1);
		}
		bitmask[16] = '\n';

	}

	return err;
}
int32_t bPer_encode_rtcpNack(BPerRtcpCommon *comm, BPerBuffer *buffer) {
	if (comm == NULL || buffer == NULL)
		return 1;
	/*
	 @doc: https://tools.ietf.org/html/rfc4585#section-6.1
	 0                   1                   2                   3
	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |V=2|P|   FMT   |       PT      |          length               |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |                  SSRC of packet sender                        |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |                  SSRC of media source                         |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 :            Feedback Control Information (FCI)                 :
	 :                                                               :

	 Generic NACK
	 0                   1                   2                   3
	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 |            PID                |             BLP               |
	 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 */
	int32_t err = BPer_Ok;
	if (!bPer_buffer_require(buffer, bPer_rtcpNack_nb_bytes())) {
		return bPer_error_wrap(ERROR_RTC_RTCP, "requires %d bytes",
				bPer_rtcpNack_nb_bytes());
	}

	BPerPidBlp *chunks = (BPerPidBlp*) bPer_malloc(comm->nack->vsize * sizeof(BPerPidBlp));
	int32_t vsize = 0;

	do {
		BPerPidBlp chunk;
		chunk.in_use = BPerFalse;
		uint16_t pid = 0;
		for (int32_t i = 0; i < comm->nack->vsize; i++) {
			uint16_t sn = comm->nack->nacks[i];
			if (!chunk.in_use) {
				chunk.pid = sn;
				chunk.blp = 0;
				chunk.in_use = BPerTrue;
				pid = sn;
				continue;
			}
			if ((sn - pid) < 1) {
				bPer_info("skip seq %d", sn);
			} else if ((sn - pid) > 16) {
				// add new chunk

				chunks[vsize].blp = chunk.blp;
				chunks[vsize++].pid = chunk.pid;
				chunk.in_use = BPerFalse;
			} else {
				chunk.blp |= 1 << (sn - pid - 1);
			}
		}
		if (chunk.in_use) {

			chunks[vsize].blp = chunk.blp;
			chunks[vsize++].pid = chunk.pid;
		}

		comm->header.length = 2 + vsize;
		if (BPer_Ok != (err = bPer_encode_header_rtcpCommon(comm, buffer))) {
			err = bPer_error_wrap(err, "encode header");
			break;
		}

        bPer_write_4bytes(buffer, comm->nack->mediaSsrc);

		for (int j = 0; j < vsize; j++) {
			bPer_write_2bytes(buffer, chunks[j].pid);
			bPer_write_2bytes(buffer, chunks[j].blp);
		}
	} while (0);
	bPer_free(chunks);
	return err;
}

uint64_t bPer_rtcpNack_nb_bytes() {
	return kRtcpPacketSize;
}


void bPer_rtcpNack_init(BPerRtcpCommon *comm, uint32_t pssrc) {
	if (comm == NULL)	return;
	comm->header.padding = 0;
	comm->header.type = BPerRtcpType_rtpfb;
	comm->header.rc = 1;
	comm->header.version = kRtcpVersion;
	comm->header.length=0;
	comm->ssrc = pssrc;

    if(comm->nack) comm->nack->mediaSsrc = pssrc;
}


void bPer_create_rtcpNack(BPerRtcpCommon *comm, uint32_t pssrc) {
	if (comm == NULL)	return;	
	if (comm->nack == NULL)
		comm->nack = (BPerRtcpNack*) bPer_calloc(1, sizeof(BPerRtcpNack));
	comm->nack->capacity = 50;
	if (comm->nack->nacks == NULL)
		comm->nack->nacks = (uint16_t*) bPer_malloc(sizeof(uint16_t) * comm->nack->capacity);

    bPer_rtcpNack_init(comm,pssrc);

}

void bPer_destroy_rtcpNack(BPerRtcpCommon *comm) {
	if (comm == NULL || comm->nack == NULL)		return;

	bPer_free(comm->nack->nacks);
	comm->nack->vsize = 0;
	bPer_free(comm->nack);
}


